/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.ext.mybatis.extension;

import com.baomidou.mybatisplus.core.conditions.AbstractWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.JoinBuilder;
import org.elsfs.cloud.common.mybatis.ext.sql.JoinConditionBuilder;
import org.elsfs.cloud.common.mybatis.ext.sql.JoinType;
import org.elsfs.cloud.common.mybatis.ext.sql.SqlParameterManager;
import org.elsfs.cloud.common.mybatis.ext.sql.SqlParameterManagerAware;
import org.elsfs.cloud.common.mybatis.ext.sql.TableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.TableAliasManagerAware;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.util.lang.StringUtils;

/**
 * 联表查询包装类
 *
 * @param <E> 实体类
 * @param <R> 联表查询条件构建器
 * @param <Children> 子类
 * @param <JB> 连接构建器
 * @param <JCB> 连接条件构建器
 */
public abstract class AbstractJoinWrapper<
        E,
        R,
        Children extends AbstractJoinWrapper<E, R, Children, JB, JCB>,
        JB extends JoinBuilder<JB, JCB, Children>,
        JCB extends JoinConditionBuilder<JCB, JB, Children>>
    extends AbstractWrapper<E, R, Children>
    implements JoinWrapper<Children, JB, JCB>,
        Query<Children, E, R>,
        SqlParameterManagerAware,
        TableAliasManagerAware,
        SqlParameterManager {

  /** SQL选择字段 */
  protected final SharedString selectedFields = new SharedString();

  /** 表别名管理器 */
  protected final TableAliasManager tableAliasManager;

  /** Join构建器列表 */
  protected final List<JB> joinBuilders = Lists.newArrayList();

  /** 是否为自定义SQL选择字段 */
  protected boolean defaultSelectedFieldsCleared;

  /**
   * 构造方法
   *
   * @param entity 实体类
   */
  public AbstractJoinWrapper(E entity) {
    super.setEntity(entity);
    super.initNeed();
    this.tableAliasManager = new SimpleTableAliasManager();
    this.tableAliasManager.registerAlias(super.getEntityClass(), JoinWrapper.TABLE_ALIAS);
  }

  /**
   * 构造方法
   *
   * @param entityClass 实体类
   */
  public AbstractJoinWrapper(Class<E> entityClass) {
    super.setEntityClass(entityClass);
    super.initNeed();

    this.tableAliasManager = new SimpleTableAliasManager();
    this.tableAliasManager.registerAlias(entityClass, JoinWrapper.TABLE_ALIAS);
  }

  /**
   * 构造方法
   *
   * @param entityClass 实体类
   * @param tableAliasManager 表别名管理器
   */
  public AbstractJoinWrapper(Class<E> entityClass, TableAliasManager tableAliasManager) {
    super.setEntityClass(entityClass);
    super.initNeed();

    this.tableAliasManager = tableAliasManager;
    this.tableAliasManager.registerAlias(entityClass, JoinWrapper.TABLE_ALIAS);
  }

  /**
   * 非对外公开的构造方法,只用于生产嵌套 sql
   *
   * @param entityClass 本不应该需要的
   */
  protected AbstractJoinWrapper(
      E entity,
      Class<E> entityClass,
      AtomicInteger paramNameSeq,
      Map<String, Object> paramNameValuePairs,
      MergeSegments mergeSegments,
      SharedString lastSql,
      SharedString sqlComment,
      SharedString sqlFirst,
      TableAliasManager tableAliasManager) {
    super.setEntity(entity);
    super.setEntityClass(entityClass);
    this.paramNameSeq = paramNameSeq;
    this.paramNameValuePairs = paramNameValuePairs;
    this.expression = mergeSegments;
    this.lastSql = lastSql;
    this.sqlComment = sqlComment;
    this.sqlFirst = sqlFirst;
    this.tableAliasManager = tableAliasManager;

    this.tableAliasManager.registerAlias(entityClass, JoinWrapper.TABLE_ALIAS);
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JB join(Class<? extends BaseEntity> entityClass) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB join(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB join(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JB join(String table) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.INNER_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB join(String table, String tableAlias) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.INNER_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB join(String table, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(typedThis, JoinType.INNER_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB join(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.INNER_JOIN);
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(Class<? extends BaseEntity> entityClass) {
    String tableName = TableInfoHelper.getTableInfo(entityClass).getTableName();
    String tableAlias = tableAliasManager.generateAlias(tableName);

    // 注册实体类与别名的映射
    tableAliasManager.registerAlias(entityClass, tableAlias);

    JB joinBuilder =
        getNewJoinBuilder(
            typedThis, JoinType.LEFT_JOIN, tableName, tableAlias, false // 不在内部重复注册
            );

    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(String table) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.LEFT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(String table, String tableAlias) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.LEFT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(String table, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(typedThis, JoinType.LEFT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB leftJoin(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.LEFT_JOIN);
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(Class<? extends BaseEntity> entityClass) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(
            typedThis,
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(String table) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.RIGHT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(String table, String tableAlias) {
    JB joinBuilder = getNewJoinBuilder(typedThis, JoinType.RIGHT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(String table, String tableAlias, boolean registerAlias) {
    JB joinBuilder =
        getNewJoinBuilder(typedThis, JoinType.RIGHT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JB rightJoin(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.RIGHT_JOIN);
  }

  /**
   * 子查询JOIN
   *
   * @param sqlBuilderConsumer 子查询SQL构建器
   * @param tableAlias 临时表别名
   * @param joinType 连接类型
   * @return 连接配置对象
   */
  protected JB doSubQueryJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias, JoinType joinType) {
    if (StringUtils.isBlank(tableAlias)) {
      throw new MybatisPlusException("JOIN子查询的表别名不能为空");
    }

    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);

    JB joinBuilder =
        getNewJoinBuilder(
            typedThis, joinType, "(" + standardSelectSql.buildSqlFragment() + ")", tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 过滤查询的字段信息(主键除外!)
   *
   * <p>例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))
   *
   * <p>例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)
   *
   * <p>例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)
   *
   * <p>例4: 要全部字段 -> select(i -> true)
   *
   * <p>例5: 只要主键字段 -> select(i -> false)
   *
   * @param entityClass 实体类
   * @param predicate 过滤方式
   * @return children
   */
  @Override
  public Children select(Class<E> entityClass, Predicate<TableFieldInfo> predicate) {
    super.setEntityClass(entityClass);
    String tableAlias = this.tableAliasManager.fetchTableAlias(entityClass);
    // 处理表别名
    String selectedFields =
        TableInfoHelper.getTableInfo(getEntityClass()).getFieldList().stream()
            .filter(predicate)
            .map(f -> tableAlias + "." + f.getColumn())
            .collect(Collectors.joining(", "));
    this.selectedFields.setStringValue(selectedFields);
    return typedThis;
  }

  /**
   * 获取JOIN的SQL片段
   *
   * @return SQL片段
   */
  @Override
  public String getJoinSqlSegment() {
    return this.buildSqlFragment();
  }

  /**
   * 是否自定义选择字段
   *
   * @return bool
   */
  @Override
  public boolean isClearedDefaultSelectedFields() {
    return this.defaultSelectedFieldsCleared;
  }

  /**
   * 查询字段 SQL 片段
   *
   * @return SQL片段
   */
  @Override
  public String getSqlSelect() {
    return this.selectedFields.getStringValue();
  }

  /**
   * 自定义SQL选择字段
   *
   * @return this
   */
  @Override
  public Children clearDefaultSelectedFields() {
    this.defaultSelectedFieldsCleared = true;
    return typedThis;
  }

  /**
   * 获取SQL参数
   *
   * @return SQL参数
   */
  @Override
  public Map<String, Object> getSqlParameters() {
    return this.paramNameValuePairs;
  }

  /**
   * 获取SQL参数管理器
   *
   * @return SQL参数管理器
   */
  @Override
  public SqlParameterManager getSqlParameterManager() {
    return this;
  }

  /**
   * 获取表别名管理器
   *
   * @return 表名别管理器
   */
  @Override
  public TableAliasManager getTableAliasManager() {
    return this.tableAliasManager;
  }

  /**
   * 注册SQL参数
   *
   * @param value 参数值
   * @return 参数名称
   */
  @Override
  public String registerParameter(Object value) {
    final String genParamName = Constants.WRAPPER_PARAM + paramNameSeq.incrementAndGet();
    final String paramStr = getParamAlias() + Constants.WRAPPER_PARAM_MIDDLE + genParamName;
    paramNameValuePairs.put(genParamName, value);
    return paramStr;
  }

  protected abstract JB getNewJoinBuilder(Children children, JoinType joinType, String table);

  protected abstract JB getNewJoinBuilder(
      Children children, JoinType joinType, String table, String tableAlias, boolean registerAlias);

  protected abstract JB getNewJoinBuilder(
      Children children, JoinType joinType, String table, String tableAlias);
}
